﻿using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Collections;
using System.Threading;
using System.IO;
using System.Windows.Forms;
using System.Data;
using System.Net;
using System.Diagnostics;

namespace Client
{
    public class ClientProgram
    {
        #region -  SOCKET  -
        private TcpClient client = null;
        private IPEndPoint ep = null;
        private NetworkStream ns = null;
        StreamWriter sw = null;
        public Queue sendQueue = null;
        private FileStream fs = null;
        private string filePath = null;
        private MyMessage message = null;
        private DataPackage package = null;
        private ProgressBar progressBar = null;
        private USBDetect usb = null;
        private AttendenceCheck attendenceCheck = null;
        private SaveFile saveFile = null;
        private bool messageShow = false;
        private bool progressBarShow = false;
        public bool isExit = false;
        private bool isAttendenceCheck = false;
        private bool isSendReceiveFile = true;
        private List<string> listDenyProcesses = null;
        private int timeout = 5000;
        private int startConnect;
        #endregion

        #region -  THREADS  -
        private Thread tMainThread = null;
        private Thread tSendThread = null;
        private Thread tReceiveThread = null;
        private Thread tSaveFile = null;
        private Thread tTimeOut = null;
        #endregion

        #region -  EVENTS  -
        public event SendFilenameEventHandler OnSendFilename = null;
        public event SendFilePercentEventHandler OnSendFilePercent = null;
        public event ReceiveFilenameEventHandler OnReceiveFilename = null;
        public event ReceiveFilePercentEventHandler OnReceiveFilePercent = null;
        public event ReceiveMessageEventHandler OnReceiveMessage = null;
        public event ActiveAttendenceCheckEventHandler OnActiveAttendenceCheck = null;
        public event RestartClientEventHandler OnRestartClient = null;
      //  public event MyPopUpEventHandler OnPopUp = null;
        #endregion

        #region -  CONTRUCTOR  -
        public ClientProgram(string ipAddress)
        {
            this.client = new TcpClient();
            this.sendQueue = new Queue();
            this.ep = new IPEndPoint(IPAddress.Parse(ipAddress), 5000);//192.168.6.147
            this.usb = new USBDetect();
            this.listDenyProcesses = new List<string>();
            ListProcesses.listProcesses = this.listDenyProcesses;
            ListProcesses.OnGetListProcesses += new GetListProcessesEventHandler(ListProcesses_OnGetListProcesses);
            ListProcesses.Initialize();
            this.usb.OnUSBDetected += new USBDetectedEventHandler(usb_OnUSBDetected);
            this.tMainThread = new Thread(new ThreadStart(StartClient));
            this.tMainThread.Start();
            this.saveFile = new SaveFile();
            this.saveFile.OnSaveFile += new SaveFileEventHandler(saveFile_OnSaveFile);
        }

        void saveFile_OnSaveFile(object sender, SaveFileEventArgs e)
        {
            this.filePath = e.FilePath;
        }

        void ListProcesses_OnGetListProcesses(object sender, GetListProcessesEventArgs e)
        {
            this.package = new DataPackage(Command.Cmd, null, e.List.Length);
            this.sendQueue.Enqueue(this.package);
            this.sendQueue.Enqueue(CommandType.GetListProcess);
            this.sendQueue.Enqueue(Encoding.ASCII.GetBytes(e.List));
        }

        void usb_OnUSBDetected(object sender, USBDetectedEventArgs e)
        {
            this.package = new DataPackage(Command.USBDectect, null, 0);
            this.sendQueue.Enqueue(this.package);
        }
        #endregion

        #region -  START CLIENT  -
        public void StartClient()
        {
            try
            {
                //Test connect
                this.tTimeOut = new Thread(new ThreadStart(ConnectTimeOut));
                this.tTimeOut.Start();
                this.startConnect = Environment.TickCount;
                //Wait connect to server
                this.client.Connect(this.ep);
                //Terminal thread
                if (this.tTimeOut != null)
                {
                    this.tTimeOut.Abort();
                }
                Thread.Sleep(10);
                this.client.ReceiveTimeout = 60000;//Receive Timeout 1 minute
                this.client.SendTimeout = 60000;//Send Timeout 1 minute
                this.ns = this.client.GetStream();
                sw = new StreamWriter(ns);
                this.tSendThread = new Thread(new ThreadStart(Send));
                this.tSendThread.Priority = ThreadPriority.Lowest;
                this.tSendThread.Start();
                this.tReceiveThread = new Thread(new ThreadStart(Receive));
                this.tReceiveThread.Priority = ThreadPriority.Lowest;
                this.tReceiveThread.Start();
                this.SetMessageBox();
                this.SetProgressBar();
                //Terminal Thread
                if (this.tMainThread != null)
                {
                    this.tMainThread.Abort();
                }
            }
            catch
            {
            }
        }
        #endregion

        #region -  CONNECT TIME OUT  -
        private void ConnectTimeOut()
        {
            while (true)
            {
                Thread.Sleep(100);
                if (Environment.TickCount > timeout + startConnect)
                {
                    MessageBox.Show("Không kết nối được tới server.", "Thông Báo", MessageBoxButtons.OK,
                         MessageBoxIcon.Error); 
                    if (this.tTimeOut != null)
                    {
                        this.tTimeOut.Abort();
                    }                                       
                    break;
                }
            }
        }
        #endregion

        #region -  STOP CLIENT  -
        public void StopClient()
        {
            try
            {
                this.client.Close();
                Thread.Sleep(500);
                if (this.tSendThread != null && this.tSendThread.IsAlive)
                {
                    this.tSendThread.Abort();
                }
                if (this.tReceiveThread != null && this.tReceiveThread.IsAlive)
                {
                    this.tReceiveThread.Abort();
                }
                if (this.tSaveFile != null)
                {
                    this.tSaveFile.Abort();
                }
                if (this.tMainThread != null && this.tMainThread.IsAlive)
                {
                    this.tMainThread.Abort();
                }
                ListProcesses.StopDenyProcess();
            }
            catch
            {
            }
        }
        public void StopClient1()
        {
            try
            {
                Thread.Sleep(500);
                if (this.tSendThread != null && this.tSendThread.IsAlive)
                {
                    this.tSendThread.Abort();
                }
                if (this.tReceiveThread != null && this.tReceiveThread.IsAlive)
                {
                    this.tReceiveThread.Abort();
                }
                if (this.tSaveFile != null)
                {
                    this.tSaveFile.Abort();
                }
                if (this.tMainThread != null && this.tMainThread.IsAlive)
                {
                    this.tMainThread.Abort();
                }
                ListProcesses.StopDenyProcess();
            }
            catch
            {
            }
        }
        #endregion

        #region -  SEND  -
        public void Send()
        {
            try
            {
                int totalBytes = 0;
                int currentBytes = 0;
                int readBytes = 0;
                int percent = 0;
                byte[] buffer = null;
                DataPackage package = null;
                while (true)
                {
                    Thread.Sleep(10);
                    if (!this.isSendReceiveFile)
                        this.isSendReceiveFile = true;
                    while (this.sendQueue.Count > 0)
                    {
                        //Send Package
                        package = new DataPackage();
                        package = (DataPackage)this.sendQueue.Dequeue();
                        buffer = package.ToByteHeader();
                        this.ns.Write(buffer, 0, buffer.Length);
                        this.ns.Flush();
                        switch (package.Cmd)
                        {
                            //Send command
                            case Command.Cmd:
                                while (this.sendQueue.Count == 0) { }
                                CommandType cmdType = (CommandType)this.sendQueue.Dequeue();
                                switch (cmdType)
                                {
                                    case CommandType.Restart:
                                        buffer = BitConverter.GetBytes((int)CommandType.Restart);
                                        break;
                                    case CommandType.Shutdown:
                                        buffer = BitConverter.GetBytes((int)CommandType.Shutdown);
                                        break;
                                    case CommandType.Standby:
                                        buffer = BitConverter.GetBytes((int)CommandType.Standby);
                                        break;
                                    case CommandType.LogOff:
                                        buffer = BitConverter.GetBytes((int)CommandType.LogOff);
                                        break;
                                    case CommandType.Hibernate:
                                        buffer = BitConverter.GetBytes((int)CommandType.Hibernate);
                                        break;
                                    case CommandType.LockComputer:
                                        buffer = BitConverter.GetBytes((int)CommandType.LockComputer);
                                        break;
                                    case CommandType.ViewClientDesktop:
                                        buffer = BitConverter.GetBytes((int)CommandType.ViewClientDesktop);
                                        this.ns.Write(buffer, 0, 4);
                                        this.ns.Flush();
                                        while (this.sendQueue.Count == 0) { }
                                        buffer = (byte[])this.sendQueue.Dequeue();
                                        break;
                                    case CommandType.GetListProcess:
                                        buffer = BitConverter.GetBytes((int)CommandType.GetListProcess);
                                        this.ns.Write(buffer, 0, 4);
                                        this.ns.Flush();
                                        while (this.sendQueue.Count == 0) { }
                                        buffer = (byte[])this.sendQueue.Dequeue();
                                        break;
                                    case CommandType.AttendenceCheck:
                                        buffer = BitConverter.GetBytes((int)CommandType.AttendenceCheck);
                                        this.ns.Write(buffer, 0, 4);
                                        this.ns.Flush();
                                        while (this.sendQueue.Count == 0) { }
                                        buffer = Encoding.UTF8.GetBytes(this.sendQueue.Dequeue().ToString());
                                        break;
                                }
                                this.ns.Write(buffer, 0, buffer.Length);
                                this.ns.Flush();
                                break;

                            case Command.Data:
                                if (this.OnSendFilename != null)
                                {
                                    this.OnSendFilename(this, new FilenameEventArgs(this.filePath));
                                }
                                this.fs = new FileStream(this.filePath, FileMode.Open, FileAccess.Read);
                              //  this.fs = new FileStream(this.filePath, FileMode.CreateNew, FileAccess.Write);
                                currentBytes = 0;
                                totalBytes = (int)this.fs.Length;
                                while (currentBytes < totalBytes & this.isSendReceiveFile)
                                {
                                    buffer = new byte[1024];
                                    readBytes = this.fs.Read(buffer, 0, buffer.Length);
                                    this.ns.Write(buffer, 0, readBytes);
                                    currentBytes += readBytes;
                                    percent = (int)(Math.Round(((double)currentBytes / (double)totalBytes) * 100, 1));
                                    if (this.OnSendFilePercent != null)
                                    {
                                        this.OnSendFilePercent(this, new PercentEventArgs(percent));
                                    }
                                    
                                }
                                
                                break;
                            case Command.Text:
                                while (this.sendQueue.Count == 0)
                                { }
                                buffer = Encoding.UTF8.GetBytes(this.sendQueue.Dequeue().ToString());
                                this.ns.Write(buffer, 0, buffer.Length);
                                this.ns.Flush();
                                break;
                            case Command.USBDectect:
                                while (this.sendQueue.Count == 0) { }
                                package = (DataPackage)this.sendQueue.Dequeue();
                                buffer = package.ToByteHeader();
                                this.ns.Write(buffer, 0, buffer.Length);
                                this.ns.Flush();
                                break;
                        }
                    }
                }
            }
            catch
            {
                //if (this.OnRestartClient != null)
                //{
                //    this.OnRestartClient(this, new EventArgs());
                //}
            }
        }
        #endregion

        #region -  RECEIVE  -
        public void Receive()
        {
            try
            {
                int totalBytes = 0;
                int currentBytes = 0;
                int percent = 0;
                int receiveBytes = 0;
                byte[] buffer = null;
                byte[] temp = null;
                string mess = null;
                DataPackage package = null;
                FileInfo fileInfo = null;
                int tam = 0;
                while (true)
                {
                    try
                    {
                        Thread.Sleep(10);
                        buffer = new byte[1024];
                        receiveBytes = this.ns.Read(buffer, 0, buffer.Length);
                        this.ns.Flush();
                        if (receiveBytes == 0)
                        {
                            this.isExit = true;
                            this.StopClient();
                            break;
                        }
                        temp = new byte[receiveBytes];
                        Array.Copy(buffer, 0, temp, 0, receiveBytes);
                        package = new DataPackage();
                        package.PackageHeader(temp);
                    }
                    catch { };

                    switch (package.Cmd)
                    {
                        case Command.Cmd:
                            buffer = new byte[4];
                            this.ns.Read(buffer, 0, buffer.Length);
                            CommandType cmdType = (CommandType)BitConverter.ToInt32(buffer, 0);
                            switch (cmdType)
                            {
                                case CommandType.LogOff:
                                    TurnOffComputer.LogOff();
                                    break;
                                case CommandType.Restart:
                                    TurnOffComputer.Reboot();
                                    break;
                                case CommandType.Standby:
                                    TurnOffComputer.StandBy();
                                    break;
                                case CommandType.Shutdown:
                                    TurnOffComputer.Shutdown();
                                    break;
                                case CommandType.Hibernate:
                                    TurnOffComputer.Hibernate();
                                    break;
                                case CommandType.LockComputer:
                                    TurnOffComputer.LockComputer();
                                    break;
                                case CommandType.ViewClientDesktop:
                                    buffer = CaptureScreenShot.ConvertBitmapToByteArray(CaptureScreenShot.Capture());
                                    package = new DataPackage(Command.Cmd, null, buffer.Length);
                                    this.sendQueue.Enqueue(package);
                                    this.sendQueue.Enqueue(CommandType.ViewClientDesktop);
                                    this.sendQueue.Enqueue(buffer);
                                    break;

                                case CommandType.DenyProcess:
                                    buffer = new byte[100];
                                    receiveBytes = this.ns.Read(buffer, 0, 100);
                                    this.ns.Flush();
                                    temp = new byte[receiveBytes];
                                    Array.Copy(buffer, 0, temp, 0, receiveBytes);
                                    mess = Encoding.ASCII.GetString(temp, 0, receiveBytes);
                                    //this.listDenyProcesses.Add(mess);
                                    // ListProcesses.listProcesses = this.listDenyProcesses;
                                    listDenyProcesses.Add(mess);
                                    break;
                                case CommandType.UnDenyProcess:
                                    buffer = new byte[100];
                                    receiveBytes = this.ns.Read(buffer, 0, 100);
                                    this.ns.Flush();
                                    temp = new byte[receiveBytes];
                                    Array.Copy(buffer, 0, temp, 0, receiveBytes);
                                    mess = Encoding.ASCII.GetString(temp, 0, receiveBytes);
                                    //this.listDenyProcesses.Add(mess);
                                    // ListProcesses.listProcesses = this.listDenyProcesses;
                                    foreach (string i in listDenyProcesses)
                                    {
                                        if (i == mess)
                                        {
                                            listDenyProcesses.Remove(i);
                                            break;
                                        }
                                    }
                                    break;
                                case CommandType.GetListProcess:
                                    ListProcesses.StartGetListProcesses();
                                    break;
                                case CommandType.KillProcess:
                                    buffer = new byte[100];
                                    receiveBytes = this.ns.Read(buffer, 0, 100);
                                    this.ns.Flush();
                                    temp = new byte[receiveBytes];
                                    Array.Copy(buffer, 0, temp, 0, receiveBytes);
                                    mess = Encoding.ASCII.GetString(temp, 0, receiveBytes);
                                    ListProcesses.KillPrcess(mess);
                                    break;
                                case CommandType.AttendenceCheck:
                                    if (this.OnActiveAttendenceCheck != null)
                                    {
                                        this.OnActiveAttendenceCheck(this, new ActiveAttendenceCheckEventArgs());
                                    }
                                    break;
                                case CommandType.StopGetListProcess:
                                    ListProcesses.StopGetListProcesses();
                                    break;
                            }
                            break;
          //Receive File
                        case Command.Data:
                            try
                            {
                                totalBytes = package.DataLength;
                                currentBytes = 0;
                                if (this.OnReceiveFilename != null)
                                {
                                    this.OnReceiveFilename(this, new FilenameEventArgs(package.Filename));
                                }
                                if (this.filePath == null)
                                    this.filePath = Application.StartupPath;
                                this.fs = new FileStream(this.filePath + @"\" + package.Filename,FileMode.Create, FileAccess.Write);
                                while (currentBytes < totalBytes)
                                {
                                    tam = currentBytes;
                                    buffer = new byte[1024];
                                    receiveBytes = this.ns.Read(buffer, 0, 1024);
                                    currentBytes += receiveBytes;
                                    this.ns.Flush();
                                    this.fs.Write(buffer, 0, receiveBytes);
                                    this.fs.Flush();
                                    percent = (int)(Math.Round(((double)currentBytes / (double)totalBytes) * 100, 1));
                                    if (this.OnReceiveFilePercent != null)
                                    {
                                        this.OnReceiveFilePercent(this, new PercentEventArgs(percent));
                                    }
                                }
                                switch (MessageBox.Show("Bạn vừa nhận file '" + package.Filename + "' từ server.\n Bạn có muốn mở thư mục chứa file vừa nhận không?", "Thông Báo",
                                    MessageBoxButtons.YesNo, MessageBoxIcon.Information))
                                {
                                    case DialogResult.Yes:
                                        ProcessStartInfo startInfo = new ProcessStartInfo(this.filePath);
                                        Process.Start(startInfo);
                                        
                                        break;
                                    case DialogResult.No:
                                        
                                        break;
                                }
                            }
                            catch
                            {          
                                if (this.progressBar != null)
                                {
                                    this.progressBar.Close();
                                }                      
                                MessageBox.Show("Lỗi nhận file " + package.Filename + " từ server.", "Thông Báo",MessageBoxButtons.OK, MessageBoxIcon.Error);                                
                                if (this.fs != null)
                                {
                                    this.fs.Flush();
                                    this.fs.Close();
                                    this.fs.Dispose();
                                }
                                fileInfo = new FileInfo(this.filePath + @"\" + package.Filename);
                                if (fileInfo.Exists)
                                {
                                    fileInfo.Delete();
                                }
                                if (this.OnRestartClient != null)
                                {
                                    this.OnRestartClient(this, new EventArgs());
                                }
                            }
                            break;
                        case Command.Text:
                            try
                            {
                                buffer = new byte[package.DataLength];
                                this.ns.Read(buffer, 0, buffer.Length);
                                this.ns.Flush();
                                mess = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
                                if (this.OnReceiveMessage != null)
                                {
                                    this.OnReceiveMessage(this, new MessageEventArgs(mess));
                                }
                            }
                            catch { };
                            break;
                        case Command.USBDectect:
                            if (package.DataLength == 1)
                            {
                                this.usb.Start();
                            }
                            else
                            {
                                this.usb.Stop();
                            }
                            break;
                    }
                }
            }
            catch (SocketException ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

        #region -  SHOW SAVE FILE DIALOG  -
        private void ShowSaveFile()
        {
            this.saveFile.ShowDialog();
            if (this.tSaveFile != null)
            {
                this.tSaveFile.Abort();
            }
        }
        #endregion

        #region -  ATTENDENCE CHECK  -
        public void SetAttendenceCheck()
        {
            if (this.attendenceCheck == null)
            {
                this.attendenceCheck = new AttendenceCheck();
                this.attendenceCheck.OnAttendenceCheck += new AttendenceCheckEventHandler(attendenceCheck_OnAttendenceCheck);
                this.attendenceCheck.OnFormExit += new FormExitEventHandler(attendenceCheck_OnFormExit);
            }
        }

        public void ShowAttendenceCheck()
        {
            if (this.attendenceCheck != null && !this.isAttendenceCheck)
            {
                this.isAttendenceCheck = true;
                Application.Run(this.attendenceCheck);
            }
        }

        void attendenceCheck_OnFormExit(object sender, FormExitEvenArgs e)
        {
            this.isAttendenceCheck = false;
            this.attendenceCheck = null;
        }

        void attendenceCheck_OnAttendenceCheck(object sender, AttendenceCheckEventArgs e)
        {
            this.package = new DataPackage(Command.Cmd, null, 4);
            this.sendQueue.Enqueue(this.package);
            this.sendQueue.Enqueue(CommandType.AttendenceCheck);
            this.sendQueue.Enqueue(e.Mssv + " " + e.Name);
        }
        #endregion

        #region -  SET MESSAGE BOX  -
        public void SetMessageBox()
        {
            try
            {
                if (this.message == null)
                {
                    this.message = new MyMessage();
                    this.message.OnSendMessage += new SendMessageEventHandler(message_OnSendMessage);
                    this.message.OnFormExit += new FormExitEventHandler(message_OnFormExit);
                }
            }
            catch
            {
            }
        }

        public void SetMessage(string mess)
        {
            this.message.SetMessage(mess);
        }

        void message_OnFormExit(object sender, FormExitEvenArgs e)
        {
            this.message = null;
            this.messageShow = false;
        }

        void message_OnSendMessage(object sender, MessageEventArgs e)
        {
            this.package = new DataPackage(Command.Text, null, e.Message.Length);
            this.sendQueue.Enqueue(this.package);
            this.sendQueue.Enqueue(e.Message);
        }

        public void ShowMessageBox()
        {
            if (this.message != null && !this.messageShow)
            {
                this.messageShow = true;
                Application.Run(this.message);
            }
        }

        public void ShowMessageBox1()
        {
            if (this.message != null && !this.messageShow)
            {
                this.messageShow = true;
                this.message.Show();
            }
        }
        #endregion

        #region -  SET PROGRESS BAR  -
        public void SetProgressBar()
        {
            try
            {
                if (this.progressBar == null)
                {
                    this.progressBar = new ProgressBar();
                    this.progressBar.OnFormExit += new FormExitEventHandler(progressBar_OnFormExit);
                    this.progressBar.OnStopSendFile += new StopSendFileEventHandler(progressBar_OnStopSendFile);
                }
            }
            catch
            { }
        }

        void progressBar_OnStopSendFile(object sender, EventArgs e)
        {
            this.isSendReceiveFile = false;
        }

        public void SetProgressBarValue(int percent)
        {
            if (this.progressBar != null)
            {
                this.progressBar.SetProgressBarValue(percent);
            }
        }

        public void SetProgressBarMessage(string message)
        {
            if (this.progressBar != null)
            {
                this.progressBar.SetMessage(message);
            }
        }

        public void SetProgressBarTitle(string title)
        {
            if (this.progressBar != null)
            {
                this.progressBar.SetTitle(title);
            }
        }

        public void ShowProgressBar()
        {
            if (this.progressBar != null && !this.progressBarShow)
            {
                this.progressBarShow = true;
                Application.Run(this.progressBar);
            }
        }

        void progressBar_OnFormExit(object sender, FormExitEvenArgs e)
        {
            this.progressBar = null;
            this.progressBarShow = false;
        }
        #endregion

        #region -  SET FILE PATH  -
        public void SetFilePath(string filePath)
        {
            this.filePath = filePath;
        }
        #endregion

        #region -  GET IP ADDRESS  -
        public string GetIPAddress()
        {
            try
            {
                string ip = this.client.Client.RemoteEndPoint.ToString();//.Split(':')[0];
                return ip;
            }
            catch
            {
                return null;
            }
        }
        #endregion

        #region -  CONNECTED  -
        public bool Connected()
        {
            try
            {
                return this.client.Connected;
            }
            catch
            {
                return false;
            }
        }
        #endregion
    }
}



